package com.bestcem.xm.wallet.service.impl;

import cn.hutool.core.lang.ObjectId;
import com.alibaba.fastjson.JSON;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.enums.ReqErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ResourceErrorCodeEnum;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.component.security.dto.TokenDTO;
import com.bestcem.xm.wallet.config.properties.WalletConfig;
import com.bestcem.xm.wallet.constant.CommonConstant;
import com.bestcem.xm.wallet.dao.LimitOrderDao;
import com.bestcem.xm.wallet.enums.*;
import com.bestcem.xm.wallet.grpc.client.BpWechatGrpcService;
import com.bestcem.xm.wallet.grpc.client.OrganizationWalletConfigGrpcService;
import com.bestcem.xm.wallet.grpc.client.WalletBaseUserGrpcService;
import com.bestcem.xm.wallet.grpc.client.WalletWalletGrpcService;
import com.bestcem.xm.wallet.grpc.client.dto.*;
import com.bestcem.xm.wallet.grpc.client.param.RechargeMiniProgramParam;
import com.bestcem.xm.wallet.service.LimitOrderService;
import com.bestcem.xm.wallet.service.dto.LimitOrderDTO;
import com.bestcem.xm.wallet.service.dto.MiniProgramPayDTO;
import com.bestcem.xm.wallet.service.dto.WalletRechargeInfoDTO;
import com.bestcem.xm.wallet.util.business.EnvSettingUtil;
import com.bestcem.xm.wallet.util.convert.LimitOrderConvert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.UUID;

/**
 * 限制充值订单service实现
 *
 * @author wangyunyun <yunyun.wang@idiaoyan.com>
 * @version v1.0
 * @date 2022/5/13 10:32
 */
@Slf4j
@Service
public class LimitOrderServiceImpl implements LimitOrderService {

    @Resource
    private LimitOrderConvert limitOrderConvert;

    @Resource
    private EnvSettingUtil envSettingUtil;

    //@Resource
    //private WalletDelayMessageSender walletDelayMessageSender;

    @Resource
    private LimitOrderDao limitOrderDao;

    @Resource
    private WalletBaseUserGrpcService walletBaseUserGrpcService;

    @Autowired
    private OrganizationWalletConfigGrpcService organizationWalletConfigGrpcService;

    @Resource
    private WalletWalletGrpcService walletWalletGrpcService;

    @Autowired
    private BpWechatGrpcService bpWechatGrpcService;

    @Autowired
    private WalletConfig walletConfig;

    @Override
    public ServiceResult<String> insert(LimitOrderDTO limitOrderDTO) {
        // 校验参数
        if (limitOrderDTO == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "参数不能为空");
        }
        if (!ObjectId.isValid(limitOrderDTO.getOrgId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "公司id参数格式不正确");
        }
        // 新增并设置返回值
        String limitOrderId = limitOrderDao.insert(limitOrderConvert.dto2Do(limitOrderDTO));
        limitOrderDTO.setLimitOrderId(limitOrderId);
        return ServiceResult.success(limitOrderId);
    }

    @Override
    public ServiceResult<Integer> updateByIdAndOrgId(LimitOrderDTO limitOrderDTO) {
        // 校验参数
        if (limitOrderDTO == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "参数不能为空");
        }
        if (!ObjectId.isValid(limitOrderDTO.getLimitOrderId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "id参数格式不正确");
        }
        if (!ObjectId.isValid(limitOrderDTO.getOrgId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "公司id参数格式不正确");
        }
        // 设置更新参数
        LimitOrderDTO condition = new LimitOrderDTO();
        condition.setOrgId(limitOrderDTO.getOrgId());
        condition.setLimitOrderId(limitOrderDTO.getLimitOrderId());
        // 更新
        return this.updateByCondition(limitOrderDTO, condition);
    }

    @Override
    public ServiceResult<Integer> updateByCondition(LimitOrderDTO param, LimitOrderDTO condition) {
        return ServiceResult.success(limitOrderDao
                .updateByCondition(limitOrderConvert.dto2Do(param), limitOrderConvert.dto2Do(condition)));
    }

    @Override
    public ServiceResult<LimitOrderDTO> getByIdAndOrgId(String limitOrderId, String orgId) {
        // 校验参数
        if (!ObjectId.isValid(limitOrderId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "id参数格式不正确");
        }
        if (StringUtils.isNotBlank(orgId) && !ObjectId.isValid(orgId)) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "公司id参数格式不正确");
        }
        // 设置查询参数并查询
        LimitOrderDTO limitOrderDTO = new LimitOrderDTO();
        limitOrderDTO.setOrgId(orgId);
        limitOrderDTO.setLimitOrderId(limitOrderId);
        ServiceResult<List<LimitOrderDTO>> serviceResult = this.findByCondition(limitOrderDTO);
        // 设置返回值
        if (CollectionUtils.isEmpty(serviceResult.getData())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NOT_FOUND, String.format("未查询到对应的充值订单, order_id = %s", limitOrderId));
        }

        LimitOrderDTO limitOrder = serviceResult.getData().get(0);
        // 待支付状态则主动轮询企业钱包
        if (LimitOrderStatusEnum.UNPAID.getStatus().equals(limitOrder.getStatus())) {
            // 获取充值订单
            RechargeInfoDTO rechargeOrder = StringUtils.isNotBlank(limitOrder.getWalletOrderId())
                    ? walletWalletGrpcService.getRechargeOrder(limitOrder.getWalletOrderId()) : null;
            if (rechargeOrder != null && WalletRechargeOrderStatusEnum.getByStatus(rechargeOrder.getStatus()) != null) {
                // 获取充值订单成功, 状态发生变更: 更新限制订单状态
                LimitOrderDTO param = new LimitOrderDTO();
                param.setLimitOrderId(limitOrder.getLimitOrderId());
                param.setOrgId(limitOrder.getOrgId());
                param.setStatus(WalletRechargeOrderStatusEnum.getByStatus(rechargeOrder.getStatus()).getLimitOrderStatus());
                this.updateByIdAndOrgId(param);
                limitOrder.setStatus(param.getStatus());
            }
        }
        return ServiceResult.success(limitOrder);
    }

    @Override
    public ServiceResult<List<LimitOrderDTO>> findByCondition(LimitOrderDTO limitOrderDTO) {
        return ServiceResult.success(limitOrderConvert.dos2Dtos(
                limitOrderDao.selectBySelective(limitOrderConvert.dto2Do(limitOrderDTO))));
    }

    @Override
    public ServiceResult<WalletRechargeInfoDTO> rechargeWallet(WalletRechargeDTO walletRechargeDTO, TokenDTO tokenDTO) {
        // 基础参数校验
        if (walletRechargeDTO.getAmount() == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "充值金额不能为空");
        }
        if (StringUtils.isBlank(walletRechargeDTO.getPath())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "path cannot be blank");
        }
        // 根据公司id查询公司钱包配置
        OrganizationWalletConfigDTO orgWalletConfigDTO = organizationWalletConfigGrpcService.getOrgWalletConfig(tokenDTO.getOrgId());
        if (orgWalletConfigDTO == null) {
            return ServiceResult.fail("查询公司充值限制失败，充值失败");
        } else if (OrgWalletConfigRechargeLimitEnum.NO_LIMIT.getType().equals(orgWalletConfigDTO.getRechargeLimit())) {
            // 钱包余额充值: 没有充值限制
            return this.rechargeWalletWithoutLimit(walletRechargeDTO, tokenDTO);
        } else if (OrgWalletConfigRechargeLimitEnum.ONLY_ORG_ACCOUNT.getType().equals(orgWalletConfigDTO.getRechargeLimit())) {
            // 钱包余额充值: 仅限本租户账号可充值
            return this.rechargeWalletWithLimit(walletRechargeDTO, tokenDTO);
        } else {
            return ServiceResult.fail("公司充值限制配置错误，充值失败");
        }
    }

    @Override
    public ServiceResult<MiniProgramPayDTO> rechargeLimitWallet(String limitOrderId, String openId, TokenDTO tokenDTO) {
        LimitOrderDTO limitOrderParam = new LimitOrderDTO();
        limitOrderParam.setOrgId(tokenDTO.getOrgId());
        limitOrderParam.setLimitOrderId(limitOrderId);
        // 查询对应的限制充值订单并校验
        LimitOrderDTO limitOrderDTO = this.getByIdAndOrgId(limitOrderParam.getLimitOrderId(), null).getData();
        if (limitOrderDTO == null) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NO_DATA, "未查询到对应的订单，充值请求失败");
        }
        if (!LimitOrderStatusEnum.CREATED.getStatus().equals(limitOrderDTO.getStatus())
                && !LimitOrderStatusEnum.UNPAID.getStatus().equals(limitOrderDTO.getStatus())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.STATUS_CHANGED, "订单状态已变更，请重新扫码支付");
        }
        // 根据orgId查询公司信息并校验
        OrganizationDTO organization = walletBaseUserGrpcService.getOrganization(limitOrderParam.getOrgId());
        if (organization == null || StringUtils.isBlank(organization.getCorpWallet())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NO_DATA, "未查询到对应租户下的企业钱包id信息");
        }
        if (!limitOrderParam.getOrgId().equals(limitOrderDTO.getOrgId())) {
            OrganizationDTO orderOrg = walletBaseUserGrpcService.getOrganization(limitOrderDTO.getOrgId());
            // 更新限制充值订单状态为已取消
            LimitOrderDTO updateLimitOrder = new LimitOrderDTO();
            updateLimitOrder.setOrgId(limitOrderDTO.getOrgId());
            updateLimitOrder.setLimitOrderId(limitOrderDTO.getLimitOrderId());
            updateLimitOrder.setStatus(LimitOrderStatusEnum.DIFF_ORG_CANCELLED.getStatus());
            updateLimitOrder.setOperatorId(tokenDTO.getUserId());
            this.updateByIdAndOrgId(updateLimitOrder);
            // 设置返回值
            MiniProgramPayDTO miniProgramDTO = new MiniProgramPayDTO();
            miniProgramDTO.setOrgCode(orderOrg.getCode());
            miniProgramDTO.setOrgName(orderOrg.getName());
            return ServiceResult.fail(miniProgramDTO, String.format("当前账号不属于 %s(%s) 无法完成充值，请切换账号后重试",
                    orderOrg.getName(), orderOrg.getCode()), String.valueOf(ResourceErrorCodeEnum.UPDATE_FORBID.getCode()));
        }

        RechargeMiniProgramDTO rechargeMiniProgramDTO = null;
        if (LimitOrderStatusEnum.CREATED.getStatus().equals(limitOrderDTO.getStatus())) {
            // 企业钱包小程序预充值
            RechargeMiniProgramParam param = new RechargeMiniProgramParam();
            param.setOpenId(openId);
            param.setPlatformCode(envSettingUtil.getRedPaperPlatformCode());
            param.setWalletId(organization.getCorpWallet());
            param.setAmount(limitOrderDTO.getAmount().movePointRight(2).intValue());
            param.setTitle(limitOrderDTO.getTitle());
            param.setProduct(limitOrderDTO.getProduct());
            param.setTradeType(TradeTypeEnum.JSAPI.getName());
            param.setFee(limitOrderDTO.getFee().movePointRight(2).intValue());
            param.setCallbackUrl(walletConfig.getCallbackUrl());
            param.setOpUserId(tokenDTO.getUserId());
            param.setOpUserName(tokenDTO.getRealName());
            param.setRemark("小程序充值");

            ServiceResult<RechargeMiniProgramDTO> miniProgramRechargeResult = walletWalletGrpcService.rechargeMiniProgram(param);
            if (!miniProgramRechargeResult.isSuccess()) {
                log.error("企业钱包小程序预充值异常：{}", param);
                log.error("企业钱包小程序预充值异常：{}", miniProgramRechargeResult);
                return ServiceResult.fail("充值异常");
            }
            rechargeMiniProgramDTO = miniProgramRechargeResult.getData();

            // 更新限制充值订单状态为待支付
            LimitOrderDTO updateLimitOrder = new LimitOrderDTO();
            updateLimitOrder.setOrgId(limitOrderDTO.getOrgId());
            updateLimitOrder.setLimitOrderId(limitOrderDTO.getLimitOrderId());
            updateLimitOrder.setStatus(LimitOrderStatusEnum.UNPAID.getStatus());
            updateLimitOrder.setWalletOrderId(rechargeMiniProgramDTO.getOrderId());
            updateLimitOrder.setOperatorId(tokenDTO.getUserId());
            updateLimitOrder.setSign(JSON.toJSONString(rechargeMiniProgramDTO));
            this.updateByIdAndOrgId(updateLimitOrder);
            // 发送延时消息，定期主动向企业钱包查询限制充值订单状态
            //walletDelayMessageSender.handleLimitOrderTimeout(limitOrderDTO.getLimitOrderId(), limitOrderDTO.getOrgId());
        } else if (LimitOrderStatusEnum.UNPAID.getStatus().equals(limitOrderDTO.getStatus())) {
            rechargeMiniProgramDTO = JSON.parseObject(limitOrderDTO.getSign(), RechargeMiniProgramDTO.class);
        }

        // 返回结果
        return ServiceResult.success(limitOrderConvert.convert(rechargeMiniProgramDTO));
    }

    @Override
    public ServiceResult<Void> rechargeOrderCallback(LimitOrderDTO limitOrderDTO) {
        // 基础参数校验
        if (limitOrderDTO == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST);
        }
        if (StringUtils.isBlank(limitOrderDTO.getWalletOrderId())) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_LOST, "order_id 不能为空");
        }
        if (limitOrderDTO.getStatus() == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_FORMAT, "status 不能为空");
        }
        // 通过钱包的充值订单id查询对应的限制充值订单并校验
        LimitOrderDTO queryParam = new LimitOrderDTO();
        queryParam.setWalletOrderId(limitOrderDTO.getWalletOrderId());
        List<LimitOrderDTO> limitOrderDTOList = this.findByCondition(queryParam).getData();
        if (CollectionUtils.isEmpty(limitOrderDTOList)) {
            return ServiceResult.fail("未查询到对应到对应的限制充值订单，充值订单状态回调失败");
        }
        // 更新限制充值订单状态
        LimitOrderDTO existLimitOrder = limitOrderDTOList.get(0);
        LimitOrderDTO updateLimitOrder = new LimitOrderDTO();
        updateLimitOrder.setOrgId(existLimitOrder.getOrgId());
        updateLimitOrder.setLimitOrderId(existLimitOrder.getLimitOrderId());
        updateLimitOrder.setStatus(limitOrderDTO.getStatus());
        this.updateByIdAndOrgId(updateLimitOrder);
        // 设置返回值
        return ServiceResult.success();
    }

    /**
     * 钱包余额充值: 仅限本租户账号可充值
     *
     * @param rechargeDTO 钱包充值dto
     * @param tokenDTO    用户认证信息
     * @return ServiceResult<WalletRechargeInfoDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/5/16 11:29
     */
    private ServiceResult<WalletRechargeInfoDTO> rechargeWalletWithLimit(WalletRechargeDTO rechargeDTO, TokenDTO tokenDTO) {
        // 设置创建限制充值订单参数
        LimitOrderDTO limitOrderDTO = new LimitOrderDTO();
        limitOrderDTO.setOrgId(tokenDTO.getOrgId());
        limitOrderDTO.setAmount(BigDecimal.valueOf(rechargeDTO.getAmount())
                .divide(CommonConstant.WALLET_RECHARGE_MONEY_DIVIDED, CommonConstant.WALLET_RECHARGE_MONEY_SCALE, RoundingMode.UP));
        limitOrderDTO.setFee(limitOrderDTO.getAmount()
                .multiply(CommonConstant.RECHARGE_CORPORATE_WALLET_AGENT_RATE)
                .setScale(CommonConstant.WALLET_RECHARGE_MONEY_SCALE, RoundingMode.UP));
        limitOrderDTO.setStatus(LimitOrderStatusEnum.CREATED.getStatus());
        limitOrderDTO.setProduct(rechargeDTO.getProduct());
        limitOrderDTO.setTitle(CommonConstant.WALLET_RECHARGE_TITLE);
        limitOrderDTO.setSign(UUID.randomUUID().toString());
        limitOrderDTO.setCreatorId(tokenDTO.getUserId());
        limitOrderDTO.setOperatorId(tokenDTO.getUserId());
        // 创建限制充值订单
        ServiceResult<String> serviceResult = this.insert(limitOrderDTO);
        if (!serviceResult.isSuccess()) {
            ServiceResult<WalletRechargeInfoDTO> failRt = ServiceResult.fail(serviceResult.getMsg());
            failRt.setErrorCode(serviceResult.getErrorCode());
            return failRt;
        }
        // 请求python grpc接口: 生成唤起微信小程序url
        GenerateMiniProgramUrlSchemeDTO programUrlSchemeDTO = new GenerateMiniProgramUrlSchemeDTO();
        programUrlSchemeDTO.setExpireType(ExpireTypeEnum.EXPIRE_TIME.getStatus());
        programUrlSchemeDTO.setExpireTime(DateUtils.addMinutes(DateUtil.getCommonDate(),
                CommonConstant.LIMIT_ORDER_TIMEOUT_HANDEL_DELAY_TIME).getTime() / 1000);
        JumpWxaDTO jumpWxaDTO = new JumpWxaDTO();
        jumpWxaDTO.setPath(rechargeDTO.getPath());
        jumpWxaDTO.setQuery(String.format("order_id=%s", limitOrderDTO.getLimitOrderId()));
        programUrlSchemeDTO.setJumpWxa(jumpWxaDTO);
        String codeUrl = bpWechatGrpcService.generateMiniProgramUrlScheme(programUrlSchemeDTO);
        if (StringUtils.isBlank(codeUrl)) {
            return ServiceResult.fail(ReqErrorCodeEnum.SERVER_ERROR, "生成小程序跳转链接失败");
        }
        // 发送限制充值订单到期取消延时消息
        //walletDelayMessageSender.pollLimitOrderStatus(limitOrderDTO.getLimitOrderId(), limitOrderDTO.getOrgId());
        // 设置返回值
        WalletRechargeInfoDTO rechargeInfoDTO = new WalletRechargeInfoDTO();
        rechargeInfoDTO.setPollUrl(String.format(CommonConstant.WALLET_RECHARGE_LIMIT_POLL_URL, limitOrderDTO.getLimitOrderId()));
        rechargeInfoDTO.setCodeUrl(codeUrl);
        rechargeInfoDTO.setOrderId(limitOrderDTO.getLimitOrderId());
        rechargeInfoDTO.setRechargeLimit(OrgWalletConfigRechargeLimitEnum.ONLY_ORG_ACCOUNT.getType());
        return ServiceResult.success(rechargeInfoDTO);
    }

    /**
     * 钱包余额充值: 没有充值限制
     *
     * @param rechargeDTO 钱包充值dto
     * @param tokenDTO    用户认证信息
     * @return ServiceResult<WalletRechargeInfoDTO>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/5/16 11:29
     */
    private ServiceResult<WalletRechargeInfoDTO> rechargeWalletWithoutLimit(WalletRechargeDTO rechargeDTO, TokenDTO tokenDTO) {
        // 根据orgId查询公司信息并校验
        OrganizationDTO organization = walletBaseUserGrpcService.getOrganization(tokenDTO.getOrgId());
        if (organization == null || StringUtils.isBlank(organization.getCorpWallet())) {
            return ServiceResult.fail(ResourceErrorCodeEnum.NO_DATA, "未查询到对应租户下的企业钱包id信息");
        }
        // 设置企业钱包请求参数
        // 设置充值参数: 根据充值金额和服务费率设置服务费
        rechargeDTO.setTitle(CommonConstant.WALLET_RECHARGE_TITLE);
        rechargeDTO.setFee(BigDecimal.valueOf(rechargeDTO.getAmount())
                .multiply(CommonConstant.RECHARGE_CORPORATE_WALLET_AGENT_RATE)
                .setScale(0, BigDecimal.ROUND_UP).intValue());
        // 设置交易参数
        WalletFinanceDTO walletFinanceDTO = new WalletFinanceDTO();
        walletFinanceDTO.setOpUserId(tokenDTO.getUserId());
        walletFinanceDTO.setOpUserName(tokenDTO.getRealName());
        // 余额充值
        WalletRechargeInfoDTO rechargeInfoDTO = walletWalletGrpcService.rechargeWallet(envSettingUtil.getRedPaperPlatformCode(), organization.getCorpWallet(), rechargeDTO, walletFinanceDTO);
        if (rechargeInfoDTO == null) {
            return ServiceResult.fail(ReqErrorCodeEnum.SERVER_ERROR, "企业钱包余额充值失败");
        }
        // 设置企业钱包充值请求结果API url
        rechargeInfoDTO.setPollUrl(String.format(CommonConstant.WALLET_RECHARGE_POLL_URL, rechargeInfoDTO.getOrderId()));
        rechargeInfoDTO.setRechargeLimit(OrgWalletConfigRechargeLimitEnum.NO_LIMIT.getType());
        return ServiceResult.success(rechargeInfoDTO);
    }
}
